﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Design;
using System.Collections;
using Microsoft.VisualStudio.Modeling;
using UofCASE.GreenPepe.Extensibility;
using Microsoft.VisualStudio.Modeling.Diagnostics;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.IO;
using UofCASE.AgileProductLinerDSL.Util;

namespace UofCASE.AgileProductLinerDSL {
    static class CommandHandler {


        /// <summary>
        /// Checks if the mapping command should be visible. It's visible when there's one test shape selected
        /// and its model is not mapped to an acceptance test.
        /// </summary>
        /// <param name="command"></param>
        /// <param name="currentSelection"></param>
        public static void CheckMappingCommandStatus(MenuCommand command, ICollection currentSelection) {
            command.Visible = false;
            command.Enabled = false;

            if (currentSelection.Count != 1) return;

            foreach (Object obj in currentSelection) {
                TestShape test = obj as TestShape;
                if (test != null) {
                    command.Visible = true;
                    command.Enabled = true;
                    break;
                }
            }

        }

        /// <summary>
        /// Checks if the unmapping command should be visible. It's visible when there is one test shape
        /// selected which model is mapped to a test.
        /// </summary>
        /// <param name="command"></param>
        /// <param name="currentSelection"></param>
        public static void CheckUnmappingCommandStatus(MenuCommand command, ICollection currentSelection) {
            command.Visible = false;
            command.Enabled = false;

            if (currentSelection.Count != 1) return;

            foreach (Object obj in currentSelection) {
                TestShape testShape = obj as TestShape;
                if (testShape == null) continue;
                Test test = testShape.ModelElement as Test;
                if (test != null && test.IsMapped) {
                    command.Visible = true;
                    command.Enabled = true;
                    break;
                }
            }
        }


        /// <summary>
        /// Checks if the import unmapped tests command should be visible. It does so, if GreenPepe is available and
        /// if there are umapped tests left.
        /// </summary>
        /// <param name="command"></param>
        /// <param name="store"></param>
        /// <param name="connector"></param>
        public static void CheckImportUnmappedTestsCommandStatus(MenuCommand command, Store store, GreenPepeConnector connector) {
            command.Visible = false;
            command.Enabled = false;

            if (!connector.IsGreenPepeAvailable(false)) return;

            if (connector.GetAllUnmappedTests(store).Count > 0) {
                command.Visible = true;
                command.Enabled = true;
            }

        }

        /// <summary>
        /// Maps the given test shape to an acceptance test represented by relative path and porject name.
        /// </summary>
        /// <param name="testShape"></param>
        /// <param name="relativePath"></param>
        /// <param name="uniqueProjectName"></param>
        public static void MapTest(TestShape testShape, String relativePath, String uniqueProjectName) {

            using (Transaction t = testShape.Store.TransactionManager.BeginTransaction("Map test")) {
                Test test = testShape.ModelElement as Test;
                if (test != null) {
                    test.RelativePath = relativePath;
                    test.ProjectUniqueName = uniqueProjectName;
                    test.IsMapped = true;
                    FileInfo file = new FileInfo(test.RelativePath);
                    test.Name = FileUtil.GetFileNameWithoutExtension(file);
                    t.Commit();
                }
            }
        }


        /// <summary>
        /// Unmaps the given test shape.
        /// </summary>
        /// <param name="testShape"></param>
        public static void UnmapTest(TestShape testShape) {
            using (Transaction t = testShape.Store.TransactionManager.BeginTransaction("Unmap test")) {
                Test test = testShape.ModelElement as Test;
                if (test != null) {
                    test.IsMapped = false;
                    test.RelativePath = "";
                    test.ProjectUniqueName = "";
                    test.LastTestRun = TestResult.None;
                    //test.Name = VSPackage.UnmappedTestShape;
                    //test.Store.DomainDataDirectory.GetDomainProperty(Test.NameDomainPropertyId).NotifyValueChange(test);
                    t.Commit();
                }
            }
        }

        /// <summary>
        /// Imports all acceptance tests from GreenPepe, which are not mapped to shapes yet.
        /// </summary>
        /// <param name="store"></param>
        /// <param name="connector"></param>
        internal static void ImportUnmappedTests(Store store, GreenPepeConnector connector) {
            if (!connector.IsGreenPepeAvailable(true)) return;
            IList<TestInformation> testInfos = connector.GetAllUnmappedTests(store);
            Partition model = store.Partitions.ElementAt(0).Value;
            Partition view = store.Partitions.ElementAt(1).Value;
            FeatureModel featureModel = store.ElementDirectory.FindElements<FeatureModel>().First();

            using (Transaction t = store.TransactionManager.BeginTransaction("Import accpetance tests")) {

                foreach (var testInfo in testInfos) {

                    Test test = new Test(model);
                    FeatureModelHasTests link = new FeatureModelHasTests(featureModel, test);

                    TestShape testShape = new TestShape(view);
                    testShape.Location = new PointD(10, 50);
                    RectangleD rec = testShape.AbsoluteBoundingBoxToEnsureVisible;
                    rec.X = 10;
                    rec.Y = 10;
                    testShape.AbsoluteBounds = rec;
                    PresentationViewsSubject.SetSubject(testShape, test);
                    MapTest(testShape, testInfo.RelativePath, testInfo.ProjectUniqueName);
                }

                t.Commit();
            }
        }

        /// <summary>
        /// Checks if the Run all acceptance tests command should be visible in Diagram Context Menu.
        /// Makes the command visible when selected shape is a Feature or an Alternative shape and only one
        /// item is selected.
        /// </summary>
        /// <param name="command"></param>
        /// <param name="store"></param>
        /// <param name="connector"></param>
        /// <param name="currentSelection"></param>
        public static void CheckRunAllTestsCommandStatus(MenuCommand command, Store store, GreenPepeConnector connector, ICollection currentSelection) {
            command.Visible = false;
            command.Enabled = false;

            if (!connector.IsGreenPepeAvailable(false)) return;
            
            if (currentSelection.Count != 1) return;

            foreach (Object obj in currentSelection) {
                if (obj is FeatureShape || obj is AlternativeShape) {
                     command.Visible = true;
                     command.Enabled = true;
                     break;
                }
            }

        }

        /// <summary>
        /// Runs the acceptance test mapped to the selected test shape model.
        /// </summary>
        /// <param name="connector"></param>
        /// <param name="currentSelection"></param>
        public static void RunTests(GreenPepeConnector connector, ICollection currentSelection) {
            if (!connector.IsGreenPepeAvailable(true)) return;

            TestShape testShape = null;
            foreach (Object obj in currentSelection) {
                testShape = obj as TestShape;
                break;
            }
            if (testShape == null) return;
            Test test = testShape.ModelElement as Test;
            if (test == null) return;
            List<TestInformation> testInfo = new List<TestInformation>();
            testInfo.Add(new TestInformation(test.RelativePath, test.ProjectUniqueName));

            connector.ExecuteTests(testInfo, testShape.Store, testShape.Diagram);
        }

        /// <summary>
        /// Finds all test shapes which have a child relationship to the selected alternative or
        /// feature shape. Runs the tests which are mapped by the models of those test shapes.
        /// </summary>
        /// <param name="connector"></param>
        /// <param name="currentSelection"></param>
        public static void RunAllTests(GreenPepeConnector connector, ICollection currentSelection) {
            if (!connector.IsGreenPepeAvailable(true)) return;

            NodeShape shape = null;
            foreach (Object obj in currentSelection) {
                shape = obj as NodeShape;
                break;
            }

            if (shape == null) return;
            FeatureModelElement feature = shape.ModelElement as FeatureModelElement;
            if (feature == null) return;
            IList<TestInformation> testInfo = new List<TestInformation>();
            bool allMapped = true;
            GetTests(feature, ref testInfo, ref allMapped);
            if (!allMapped) {
                System.Windows.MessageBox.Show(VSPackage.WarningNotAllTestsAreMapped, "Not all tests mapped", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Warning);
            }
            connector.ExecuteTests(testInfo, shape.Store, shape.Diagram);
        }
        
        /// <summary>
        /// Gets all tests that are a descendant of the given feature model element
        /// </summary>
        /// <param name="element"></param>
        /// <param name="testInfo"></param>
        /// <param name="allMapped"></param>
        private static void GetTests(FeatureModelElement element, ref IList<TestInformation> testInfo, ref Boolean allMapped) {
            Feature feature = element as Feature;
            if (feature != null && feature.TestPort != null) {
                foreach (var fPort in feature.TestPort.FeaturePorts) {
                    TestInformation testInform = new TestInformation(fPort.Test.RelativePath, fPort.Test.ProjectUniqueName);
                    if (!fPort.Test.IsMapped) {
                        allMapped = false;
                        continue;
                    }
                    if (!testInfo.Contains(testInform)) testInfo.Add(testInform);
                }
            }
            foreach (var subFeatureElement in element.SubFeatureModelElements) {
                GetTests(subFeatureElement, ref testInfo, ref allMapped);
            }

        }

        /// <summary>
        /// Resets the test result of all tests in the model
        /// </summary>
        /// <param name="store"></param>
        public static void ResetAllTestResults(Store store) {
            //store.ElementDirectory.FindElements<Test>();
            using (Transaction t = store.TransactionManager.BeginTransaction("SetTestResults")) {
                foreach (var test in store.ElementDirectory.FindElements<Test>()) {
                    // in case model can not be found return (not found can also mean wrong diagram)
                    test.LastTestRun = TestResult.None;
                }
                t.Commit();
            }
        }

        internal static void ShowAllTestResults(Store store) {
            foreach (var testPortShape in store.ElementDirectory.FindElements<TestPortShape>()) {
                if (testPortShape.IsCollapsed) {
                    testPortShape.ChangeCollapsing();
                }
            }

            foreach (var testS in store.ElementDirectory.FindElements<TestShape>()) {
                testS.SetShowHideState(true);
            }
        }

        internal static void HideAllTestResults(Store store) {

            foreach (var testPortShape in store.ElementDirectory.FindElements<TestPortShape>()) {
                if (!testPortShape.IsCollapsed) {
                    testPortShape.ChangeCollapsing();
                }
            }

            foreach (var testS in store.ElementDirectory.FindElements<TestShape>()) {
                testS.SetShowHideState(false);
            }
        }
    }
}
